import ctypes
import inspect
import os
import random
import subprocess
import threading
import time
import tkinter as tk
import tkinter.scrolledtext as tks
from io import BytesIO
from tkinter import messagebox
from tkinter import ttk
from tkinter.filedialog import askdirectory

import docx.shared
from PIL import Image, ImageTk
from docx import Document
from loguru import logger

import CheckTool
import WebServer
from CheckTool import DirCheck, DocxTool
from DocShow import BrowserTool
from MyConfig import MyConfig

logger.add("tool.log", retention="3 days")


@logger.catch
def _async_raise(tid, exctype):
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


class Index:
    def __init__(self):
        self.browser_thread = None
        self.webserver_thread = None
        self.dirCheck = None
        self.browser = None
        # 当前文件序号
        self.file_num = 0
        self.file_num_var = None
        # 总文件数
        self.file_count = 0
        self.file_count_var = None
        # 目录路径
        self.dir_path = None
        # 文件路径
        self.file_path = None
        # 文件名
        self.file_name = None
        # 目录下所有文件
        self.files = {}
        # 文本查看
        self.txt_edit = None
        self.label_checked = None
        self.window = tk.Tk()
        self.docx = None
        # 配置获取
        self.config = MyConfig()
        # 自动预览
        self.auto_view = None
        # 得到屏幕宽度
        self.sw = self.window.winfo_screenwidth()
        logger.info("屏幕宽度：{}", self.sw)
        # 得到屏幕高度
        self.sh = self.window.winfo_screenheight()
        logger.info("屏幕高度：{}", self.sh)
        # 目录处理进度条
        self.progressbar = None
        self.load_pop_win = None
        self.window.title("作业检查")

        self.window.rowconfigure(1, minsize=600, weight=1)
        self.window.columnconfigure(1, minsize=800, weight=1)
        self.window.geometry("1050x660+100+0")
        self.str_var_init()

    def start(self):
        self.menu_create()
        self.top_frame()
        self.floor_frame()
        self.left_frame()
        self.right_frame()
        self.quick_bind()
        self.window.mainloop()

    def restart_view_server(self, *args):
        self.stop_view_server()
        self.start_view_server()

    @logger.catch
    def stop_view_server(self):
        with os.popen('netstat -aon|findstr "8012"') as res:
            res = res.read().split('\n')
            result = []
            for line in res:
                temp = [i for i in line.split(' ') if i != '']
                if len(temp) > 4:
                    result.append(temp[4])
        for i in result:
            # os.kill(int(i), signal.SIGINT)
            os.popen('taskkill /pid ' + str(i) + ' /F')
            logger.info(f"杀死占用端口的进程成功，该进程pid：{i}")

    @logger.catch
    def start_view_server(self):
        bool = CheckTool.detect_port(8012)
        logger.info("8012端口占用情况：{}", bool)
        if not bool:
            cmd = os.path.join(os.path.abspath('.'), 'kkFileView-4.0.0/bin/startup.bat')
            process = subprocess.Popen(cmd, creationflags=subprocess.CREATE_NEW_CONSOLE)

    def quick_bind(self):
        self.window.bind_all('<KeyPress-Left>', self.open_file_pre)
        self.window.bind_all('<KeyPress-Right>', self.open_file_next)
        self.window.bind_all('<Alt-q>', self.selectPath)
        self.window.bind_all('<Alt-z>', self.check_ok)
        self.window.bind_all('<Alt-x>', self.pop_check_win)
        self.window.bind_all('<Alt-w>', self.photo_pop)
        self.window.bind_all('<Alt-s>', self.pop_view_win)

    def str_var_init(self):
        self.file_path = tk.StringVar()
        self.file_name = tk.StringVar()
        self.dir_path = tk.StringVar()
        self.auto_view = tk.IntVar()
        # self.dir_path.set(os.path.abspath("."))
        # self.dirCheck = DirCheck(os.path.abspath("."))
        file_num_str = "第{}个".format(self.file_num)
        file_count_str = "共{}个".format(self.file_count)
        self.file_num_var = tk.StringVar(value=file_num_str)
        self.file_count_var = tk.StringVar(value=file_count_str)

    def update_file_label(self):
        self.file_count = len(self.files)
        file_num_str = "第{}个".format(self.file_num)
        file_count_str = "共{}个".format(self.file_count)
        self.file_num_var.set(file_num_str)
        self.file_count_var.set(file_count_str)

    def menu_create(self):
        def pop_help():
            pop_win = tk.Toplevel()
            pop_win.title("help")
            pop_win.geometry("600x400+200+200")
            text_1 = tk.Text(pop_win)
            text_1.grid(row=0, column=0, padx=5, pady=5)
            text_1.insert(tk.END, self.config.getVal("help", "content"))

        def pop_config():
            print("config")
            # 快捷批改
            content_1 = tk.StringVar()
            content_2 = tk.StringVar()
            content_3 = tk.StringVar()
            pop_win = tk.Toplevel()
            pop_win.title("help")
            pop_win.geometry("600x150+200+200")
            tk.Label(pop_win, text="Alt-s:").grid(row=0, column=0, padx=5)
            e1 = tk.Entry(pop_win, width=70, textvariable=content_1)
            e1.grid(row=0, column=1)

            tk.Label(pop_win, text="Alt-d:").grid(row=1, column=0, padx=5)
            e2 = tk.Entry(pop_win, width=70, textvariable=content_2)
            e2.grid(row=1, column=1, columnspan=2)

            tk.Label(pop_win, text="Alt-f:").grid(row=2, column=0, padx=5)
            e3 = tk.Entry(pop_win, width=70, textvariable=content_3)
            e3.grid(row=2, column=1, columnspan=2)

            content_1.set(self.config.getContent("content_1"))
            content_2.set(self.config.getContent("content_2"))
            content_3.set(self.config.getContent("content_3"))

            def submit():
                print(content_1.get())
                print(content_2.get())
                print(content_3.get())
                self.config.setContent("content_1", content_1.get())
                self.config.setContent("content_2", content_2.get())
                self.config.setContent("content_3", content_3.get())
                self.config.save()

            def close(*args):
                pop_win.destroy()

            tk.Button(pop_win, text="确认", command=submit, width=20).grid(row=3, columnspan=2, pady=10)
            pop_win.focus_set()
            pop_win.bind("<Escape>", close)

        main_menu = tk.Menu(self.window)
        main_menu.add_command(label='配置', command=pop_config)
        main_menu.add_command(label='清理备份', command=self.clear_bak)
        main_menu.add_command(label='清理缓存', command=self.clear_cache)
        # main_menu.add_command(label='重启预览服务', command=self.restart_view_server)
        main_menu.add_command(label='帮助', command=pop_help)
        self.window.config(menu=main_menu)

    def floor_frame(self):
        frm_floor = tk.Frame(self.window, relief=tk.RAISED, bd=2)
        frm_floor.grid(row=2, column=0, columnspan=2, sticky="nsew")
        tk.Label(frm_floor, text="author：by guoyang | #20221105(1.0)").grid(row=0, column=0)

    def top_frame(self):
        frm_top = tk.Frame(self.window, relief=tk.RAISED, bd=2)
        frm_top.grid(row=0, column=0, columnspan=2, sticky="nsew")
        # -------------------------------------------
        name_label = tk.Label(frm_top, textvariable=self.file_name, bg='blue', fg='white', width=40, bd=2)
        name_label.grid(row=0, column=0)
        tk.Label(frm_top, text="目标路径:").grid(row=0, column=1)
        tk.Entry(frm_top, textvariable=self.dir_path, state="readonly").grid(row=0, column=2, ipadx=200)
        tk.Button(frm_top, text="目录选择", command=self.selectPath).grid(row=0, column=3)
        tk.Button(frm_top, text="打开目录", command=self.openPath).grid(row=0, column=4)

    def right_frame(self):
        self.txt_edit = tks.ScrolledText(self.window, wrap=tk.WORD)
        self.txt_edit.grid(row=1, column=1, sticky="nsew")

    def left_frame(self):
        frm_buttons = tk.Frame(self.window, relief=tk.RAISED, bd=2)
        frm_buttons.grid(row=1, column=0, sticky="nsew")
        # ---------------------------------------------------------------------------------
        # btn_open = tk.Button(frm_buttons, text="打开", command=self.open_file_select)
        # btn_save = tk.Button(frm_buttons, text="另存", command=self.save_file)
        btn_ok = tk.Button(frm_buttons, text="通过", command=self.check_ok)
        btn_change = tk.Button(frm_buttons, text="批改", command=self.pop_check_win)
        btn_photo = tk.Button(frm_buttons, text="图片查看", command=self.photo_pop)
        btn_pre = tk.Button(frm_buttons, text="上一个", command=self.open_file_pre)
        btn_next = tk.Button(frm_buttons, text="下一个", command=self.open_file_next)
        btn_view = tk.Button(frm_buttons, text="预览", command=self.pop_view_win)
        btn_viewAuto = tk.Checkbutton(frm_buttons, text="自动预览", variable=self.auto_view)
        btn_check_auto = tk.Button(frm_buttons, text="自动批改", command=self.check_auto)
        btn_ok_auto = tk.Button(frm_buttons, text="全部通过", command=self.ok_auto)

        self.label_checked = tk.Label(frm_buttons, text="")
        label_num = tk.Label(frm_buttons, textvariable=self.file_num_var)
        label_count = tk.Label(frm_buttons, textvariable=self.file_count_var)
        # ----------------------------------------------------------------
        # btn_open.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
        # btn_save.grid(row=1, column=0, sticky="ew", padx=5)
        btn_ok.grid(row=2, column=0, sticky="ew", padx=5)
        btn_change.grid(row=3, column=0, sticky="ew", padx=5)
        btn_pre.grid(row=4, column=0, sticky="ew", padx=5)
        btn_next.grid(row=5, column=0, sticky="ew", padx=5)
        btn_photo.grid(row=6, column=0, sticky="ew", padx=5)
        btn_view.grid(row=7, column=0, sticky="ew", padx=5)
        btn_viewAuto.grid(row=8, column=0, sticky="ew", padx=5)
        self.label_checked.grid(row=9, column=0, sticky="ew", padx=5, pady=10)
        btn_check_auto.grid(row=10, column=0, sticky="ew", padx=5)
        btn_ok_auto.grid(row=11, column=0, sticky="ew", padx=5)
        label_num.grid(row=12, column=0, sticky="ew", padx=5)
        label_count.grid(row=13, column=0, sticky="ew", padx=5)
        # ------------------------------------------------------------------

    def ok_auto(self):
        print("全部通过")
        if self.file_count < 1:
            return
        for i in range(0, self.file_count):
            self.check_ok()
            self.open_file_next()

    def check_auto(self):
        print("自动批改")
        if self.file_count < 1:
            return
        for i in range(0, self.file_count):
            self.check_auto_insert_content()
            self.open_file_next()

    def check_auto_insert_content(self, *args):
        print("check_ok")
        file_type = self.file_name.get().split(".")[-1]
        content_arr = [self.config.getContent("content_1"), self.config.getContent("content_2"),
                       self.config.getContent("content_3")]
        content = random.choice(content_arr)
        if file_type == 'txt':
            self.txt_edit.insert(tk.END, "\n" + "批改：" + content)
            self.save_text_file()
            self.is_checked("批改")
        elif file_type == 'docx':
            self.txt_edit.insert(tk.END, "\n" + "批改：" + content)
            p = self.docx.doc.add_paragraph()
            run = p.add_run("批改：" + content)
            run.bold = True
            run.font.color.rgb = docx.shared.RGBColor(250, 0, 0)
            # self.docx.doc.add_picture('正确.png', width=docx.shared.Cm(5.5))
            self.docx.doc.save(self.file_path.get())
            self.is_checked("批改")
        else:
            print("文件不支持修改！")

    def close_all_pop(self):
        for child in self.window.winfo_children():
            if isinstance(child, tk.Toplevel):
                child.destroy()

    def view_by_browser(self):
        if self.file_name.get():
            if not self.browser:
                self.browser = BrowserTool()
            dir_path = self.dir_path.get()
            file_path = self.file_path.get()
            # print(file_path.replace(dir_path, ""))
            self.browser.show(file_path.replace(dir_path, "").replace("\\", "/"))

    def pop_view_win(self, *args):
        self.browser_thread = threading.Thread(target=self.view_by_browser, ).start()

    def load_pop(self):
        self.load_pop_win = tk.Toplevel(self.window)
        self.load_pop_win.title("处理中")
        self.load_pop_win.geometry("320x80+450+200")
        self.progressbar = ttk.Progressbar(self.load_pop_win)
        self.progressbar.grid(row=0, column=0, padx=20, pady=20)
        self.progressbar['maximum'] = 100
        self.progressbar['length'] = 280
        for i in range(50):
            self.progressbar["value"] = i + 1
            self.window.update()
            time.sleep(0.001)

    @logger.catch
    def photo_pop(self, *args):
        if not self.docx or not self.docx.images or self.docx.images_num <= 0:
            return
        photo_pop_win = tk.Toplevel()
        photo_pop_win.title("图片")
        photo_pop_win.geometry("800x600+400+50")
        global num
        count = self.docx.images_num
        num = -1

        def f1(w_, h_, png_image):
            w, h = png_image.size
            f1 = 1.0 * w_ / w
            f2 = 1.0 * h_ / h
            factor = min([f1, f2])
            width = int(w * factor)
            height = int(h * factor)
            return png_image.resize((width, height), Image.ANTIALIAS)

        def down(*args):
            global num
            num = num + 1
            if num == count:
                num = 0
            # print("图片序号：%d - %d" % (num, count))
            show_pic(self.docx.images[num])

        def up(*args):
            global num
            num = num - 1
            if num < 0:
                num = count - 1
            # print("图片序号：%d - %d" % (num, count))
            show_pic(self.docx.images[num])

        def close(ev=None):
            photo_pop_win.destroy()

        def show_pic(img_file):
            img = f1(800, 600, Image.open(BytesIO(img_file.blob)))
            img = ImageTk.PhotoImage(img)
            label.configure(image=img)
            label.image = img

        photo_pop_win.bind("<Escape>", close)
        main_menu = tk.Menu(photo_pop_win)  # 创建主菜单
        main_menu.add_command(label='上一张', command=up)
        main_menu.add_command(label='下一张', command=down)
        photo_pop_win.config(menu=main_menu)

        box = tk.Frame(photo_pop_win, width=800, height=600, borderwidth=5)
        box.grid(row=0, column=0)
        label = tk.Label(box, width=800, height=600)
        label.grid(row=0, columnspan=2)
        label.focus_set()
        label.bind("<Alt-n>", up)
        label.bind("<Alt-m>", down)
        down()

    def open_file_select(self):
        print()

    @logger.catch
    def save_text_file(self):
        with open(self.file_path.get(), mode="w", encoding="utf-8") as output_file:
            text = self.txt_edit.get("1.0", tk.END)
            output_file.write(text)

    def save_file(self):
        print()

    @logger.catch
    def check_ok(self, *args):
        print("check_ok")
        file_type = self.file_name.get().split(".")[-1]
        if file_type == 'txt':
            self.txt_edit.insert(tk.END, "\n" + "批改：正确")
            self.save_text_file()
            self.is_checked("批改")
        elif file_type == 'docx':
            self.txt_edit.insert(tk.END, "\n" + "批改：正确")
            p = self.docx.doc.add_paragraph()
            run = p.add_run("批改：正确")
            run.bold = True
            run.font.color.rgb = docx.shared.RGBColor(250, 0, 0)
            self.docx.doc.add_picture('正确.png', width=docx.shared.Cm(5.5))
            self.docx.doc.save(self.file_path.get())
            self.is_checked("批改")
        else:
            print("文件不支持修改！")

    def pop_check_win(self, *args):
        print("pop_check_win")
        pop_win = tk.Toplevel()
        pop_win.title("批改")
        pop_win.geometry("600x200+200+200")
        label_1 = tk.Label(pop_win, text="批改内容：", width=10)
        label_1.grid(row=1, column=0, padx=5, pady=5)
        content = tk.StringVar()
        entry_1 = tk.Entry(pop_win, width=60, textvariable=content)
        entry_1.grid(row=1, column=1, padx=5, pady=5)
        entry_1.focus_set()

        tk.Label(pop_win, text="Alt-s：", width=10).grid(row=2, column=0, padx=5, pady=5)
        tk.Label(pop_win, text=self.config.getContent("content_1")).grid(row=2, column=1, padx=5, pady=5)
        tk.Label(pop_win, text="Alt-d：", width=10).grid(row=3, column=0, padx=5, pady=5)
        tk.Label(pop_win, text=self.config.getContent("content_2")).grid(row=3, column=1, padx=5, pady=5)
        tk.Label(pop_win, text="Alt-f：", width=10).grid(row=4, column=0, padx=5, pady=5)
        tk.Label(pop_win, text=self.config.getContent("content_3")).grid(row=4, column=1, padx=5, pady=5)

        def check():
            ele = "批改：" + entry_1.get()
            self.txt_edit.insert(tk.END, "\n" + ele)
            file_type = self.file_name.get().split(".")[-1]
            if file_type == 'txt':
                self.save_text_file()
                self.is_checked("批改")
            elif file_type == 'docx':
                print("docx保存")
                p = self.docx.doc.add_paragraph()
                run = p.add_run(ele)
                run.bold = True
                run.font.color.rgb = docx.shared.RGBColor(250, 0, 0)
                self.docx.doc.save(self.file_path.get())
                self.is_checked("批改")
            pop_win.destroy()

        def check_enter(ev=None):
            check()

        def contet1(ev=None):
            print("content1")
            content.set(self.config.getContent("content_1"))

        def contet2(ev=None):
            content.set(self.config.getContent("content_2"))

        def contet3(ev=None):
            content.set(self.config.getContent("content_3"))

        entry_1.bind("<Return>", check_enter)
        entry_1.bind('<Alt-s>', contet1)
        entry_1.bind('<Alt-d>', contet2)
        entry_1.bind('<Alt-f>', contet3)

        def close(ev=None):
            pop_win.destroy()

        pop_win.bind("<Escape>", close)
        tk.Button(pop_win, width=20, text="退出", command=close).grid(row=5, column=0, padx=5, pady=5)
        tk.Button(pop_win, width=20, text="提交", command=check).grid(row=5, column=1, padx=5, pady=5)

    @logger.catch
    def open_file_pre(self, *args):
        self.close_all_pop()
        if self.file_count == 0:
            return
        self.file_num -= 1
        if self.file_num <= 0:
            self.file_num = self.file_count
        if self.files[self.file_num]:
            self.file_path.set(self.files[self.file_num])
            dir_name, full_file_name = os.path.split(self.file_path.get())
            self.file_name.set(full_file_name)
            self.update_file_label()
        self.readFile()
        if self.auto_view.get() == 1:
            self.pop_view_win()

    @logger.catch
    def open_file_next(self, *args):
        self.close_all_pop()
        if self.file_count == 0:
            return
        self.file_num += 1
        if self.file_num > self.file_count:
            self.file_num = 1
        if self.files[self.file_num]:
            self.file_path.set(self.files[self.file_num])
            dir_name, full_file_name = os.path.split(self.file_path.get())
            self.file_name.set(full_file_name)
            self.update_file_label()
        self.readFile()
        if self.auto_view.get() == 1:
            self.pop_view_win()

    @logger.catch
    def satrtWebServer(self):
        httpd = WebServer.HTTPServer(self.dir_path.get())
        httpd.serve_forever()

    @logger.catch
    def selectPath(self, *args):
        path_ = askdirectory()
        if path_ == "":
            return
            # self.dir_path.get()
        else:
            path_ = path_.replace("/", "\\")
        logger.info("打开目录：{}", path_)
        self.dir_path.set(path_)
        self.load_pop()
        # threading.Thread(target=).start()
        if self.webserver_thread:
            stop_thread(self.webserver_thread)
        self.webserver_thread = threading.Thread(target=self.satrtWebServer, ).start()
        self.dirCheck = DirCheck(self.dir_path.get())
        self.files = self.dirCheck.list(self.progressbar, self.load_pop_win)
        self.update_file_label()
        self.open_file_next()
        self.file_num = 1

    def openPath(self):
        dir_str = os.path.dirname(self.dir_path.get() + "\\")
        os.system('start ' + dir_str)

    @logger.catch
    def readFile(self):
        file_type = self.file_name.get().split(".")[-1]
        self.txt_edit.delete("1.0", tk.END)
        text_str = ''
        if file_type.endswith("txt"):
            with open(self.file_path.get(), mode="r", encoding="utf-8") as input_file:
                text_str = input_file.read()
        elif file_type.endswith("doc"):
            print("doc文件")
        elif file_type.endswith("docx"):
            # print("docx文件")
            doc = Document(self.file_path.get())
            for no, p in enumerate(doc.paragraphs):
                # print(no, p.text)
                text_str = text_str + p.text + "\n"
            self.docx = DocxTool(doc)
            self.docx.get_all_image()
            # print("图片数量：" + str(len(self.docx.images)))
            # if len(images) > 0:
            #     pop_photo()
        else:
            text_str = "文件类型暂不支持！"
            # print("文件类型暂不支持！")
        self.is_checked(text_str)
        self.txt_edit.insert(tk.END, text_str)
        self.window.title(f"作业检查 - {self.file_path.get()}")

    def is_checked(self, text):
        if "批改" in text:
            self.label_checked.configure(text="已批改", fg="white", bg="green")
            return True
        else:
            self.label_checked.configure(text="未批改", fg="white", bg="red")
            return False

    @logger.catch
    def clear_bak(self):
        result = messagebox.askokcancel(title='注意!', message='是否清理所有的备份文件？')
        if result:
            DirCheck.clear_bak()
            messagebox.showinfo('提示', '清理成功')

    @logger.catch
    def clear_cache(self):
        result = messagebox.askokcancel(title='注意!', message='是否清理所有的文件缓存？')
        if result:
            DirCheck.clear_cache()
            messagebox.showinfo('提示', '清理成功')
